'''
This file is part of ModeliMark.

ModeliMark is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.

ModeliMark is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with ModeliMark.
If not, see <http://www.gnu.org/licenses/>.

Copyright 2012 Jens Frenkel
'''

from modelimark.wrapper import BaseWrapper
import numbers
import shlex, subprocess
import os
from time import *
from decimal import Decimal

import win32api,win32process,win32con

class OMC(BaseWrapper):
    '''
    classdocs
    '''


    def __init__(self, omcpath):
        '''
        Constructor

        :param omcpath: Path to the OMC executable.
        :type omcpath: String.
        '''

        assert(isinstance(omcpath,str))
        self._omc = omcpath

    def flatten(self, model, files, libs):
        '''
        instructs the compiler to parse the Modelica
        code and return the flat model

        :param model: Name of the Model to flatten.
        :type model: String.

        :param files: all Files of the Model to flatten.
        :type files: list of String.
        '''
        assert(isinstance(model,str))
        assert(isinstance(files,list))

        # generate mos file
        mosfile = '%s.mos'%(model)
        f = open( mosfile, 'w' )
        for lib in libs:
            f.write('loadModel(%s);\n'%(lib))
        for file in files:
            f.write('loadFile("%s");\n'%(file))
        f.write('instantiateModel(%s);\n'%(model))
        # Close File
        f.close()

        #
        logfile = '%s_flatten_py.log'%(model)
        res, dt = self._run(logfile,[mosfile])
        if (res == 0):
            res = self._readResult_flatten(logfile,len(files))
        else:
            res = False

        return res, dt

    def translate(self, model, files, libs):
        '''
        flattens the model and turns it into a state which can be simulated

        :param model: Name of the Model to flatten.
        :type model: String.

        :param files: all Files of the Model to flatten.
        :type files: list of String.
        '''
        assert(isinstance(model,str))
        assert(isinstance(files,list))

        # generate mos file
        mosfile = '%s.mos'%(model)
        f = open( mosfile, 'w' )
        for lib in libs:
            f.write('loadModel(%s);\n'%(lib))
        for file in files:
            f.write('loadFile("%s");\n'%(file))
        f.write('translateModel(%s);\n'%(model))
        # Close File
        f.close()

        #
        logfile = '%s_translate_py.log'%(model)
        res, dt = self._run(logfile,[mosfile])
        if (res == 0):
            res = self._readResult_translate(logfile,len(files))
        else:
            res = False

        return res, dt

    def simulate(self, model, files, libs, startTime, outputInterval, stopTime):
        '''
        flattens, translates and simulates the model using the standard solver
        and a predefined output interval and stop time

        :param model: Name of the Model to flatten.
        :type model: String.

        :param files: all Files of the Model to flatten.
        :type files: list of String.

        :param startTime: Start Time of the Simulation
        :type startTime: numbers.Real.

        :param outputInterval: Intervall for Output of Results.
        :type outputInterval: numbers.Real.

        :param stopTime: Stop Time of the Simulation
        :type stopTime: numbers.Real.
        '''
        assert(isinstance(model,str))
        assert(isinstance(files,list))
        assert(isinstance(startTime,numbers.Real))
        assert(isinstance(outputInterval,numbers.Real))
        assert(isinstance(stopTime,numbers.Real))

        # generate mos file
        mosfile = '%s.mos'%(model)
        f = open( mosfile, 'w' )
        for lib in libs:
            f.write('loadModel(%s);\n'%(lib))
        for file in files:
            f.write('loadFile("%s");\n'%(file))
        f.write('simulate(%s,startTime=%s,stopTime=%s,outputInterval=%s);\n'%(model,str(startTime),str(stopTime),str(outputInterval)))
        # Close File
        f.close()

        #
        logfile = '%s_simulate_py.log'%(model)
        res, dt = self._run(logfile,[mosfile])
        if (res == 0):
            res = self._readResult_simulate(logfile,len(files))
        else:
            res = False

        return res, dt

    def _run(self,logfilename,args):

        assert(isinstance(logfilename,str))

        ar = list()
        ar.append(self._omc)
        for a in args:
            ar.append(a)

        print 'starte OMC'
        f = open( logfilename, 'w' )
        t1 = clock()
        p = subprocess.Popen(args=ar,stdout=f)

        pid = p.pid
        handle = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, True, pid)
        win32process.SetPriorityClass(handle, win32process.REALTIME_PRIORITY_CLASS)

        run = True
        while run:
            res = p.poll()
            if (res is not None):
                run = False
        t2 = clock()
        dt = t2 - t1
        f.close()
        return res, dt

    def _readResult_flatten(self,logfilename,nFiles):

        assert(isinstance(logfilename,str))
        assert(isinstance(nFiles,int))

        f = open( logfilename, 'r' )
        for n in xrange(nFiles):
            ln = f.readline()
            if (ln != 'true\n'): return False
        res = list()
        firstline = f.readline()
        res.append(firstline[1:len(firstline)])
        for line in f:
            res.append(line)
        if (len(res)>1):
            res.pop(len(res)-1)

        f.close()

        return res

    def _readResult_translate(self,logfilename,nFiles):

        assert(isinstance(logfilename,str))
        assert(isinstance(nFiles,int))

        f = open( logfilename, 'r' )
        for n in xrange(nFiles):
            ln = f.readline()
            if (ln != 'true\n'): return False
        res = True


        f.close()

        return res

    def _readResult_simulate(self,logfilename,nFiles):

        assert(isinstance(logfilename,str))
        assert(isinstance(nFiles,int))

        timeFrontend = None
        timeBackend = None
        timeSimCode = None
        timeTemplates = None
        timeCompile = None
        timeSimulation = None
        timeTotal = None

        f = open( logfilename, 'r' )
        for n in xrange(nFiles):
            ln = f.readline()
            if (ln.find('timeFrontend') > -1):
                tfi = ln.find('=')
                tstr = ln[tfi+1:len(ln)-1]
                timeFrontend = float(tstr)
            elif (ln.find('timeBackend') > -1):
                tfi = ln.find('=')
                tstr = ln[tfi+1:len(ln)-1]
                timeBackend = float(tstr)
            elif (ln.find('timeSimCode') > -1):
                tfi = ln.find('=')
                tstr = ln[tfi+1:len(ln)-1]
                timeSimCode = float(tstr)
            elif (ln.find('timeTemplates') > -1):
                tfi = ln.find('=')
                tstr = ln[tfi+1:len(ln)-1]
                timeTemplates = float(tstr)
            elif (ln.find('timeCompile') > -1):
                tfi = ln.find('=')
                tstr = ln[tfi+1:len(ln)-1]
                timeCompile = float(tstr)
            elif (ln.find('timeSimulation') > -1):
                tfi = ln.find('=')
                tstr = ln[tfi+1:len(ln)-1]
                timeSimulation = float(tstr)
            elif (ln.find('timeTotal') > -1):
                tfi = ln.find('=')
                tstr = ln[tfi+1:len(ln)-1]
                timeTotal = float(tstr)
        f.close()

        return [timeFrontend,timeBackend,timeSimCode,timeTemplates,timeCompile,timeSimulation,timeTotal]



